#include "program.h"
#include <string.h>
#include <fstream>
#include "tabledriver.h"
#include "global.h"

#include <iostream>		// FOR TEST, SHOULD REMOVE LATER

const std::string ProgramManager::kProgramFileSuffix = ".PRG";

ProgramManager *ProgramManager::instance_ = NULL;


//namespace {
//
//void SwapItem(int idx1, int idx2)
//{
//	item_s *item1	= &program()->kinds[0].item[idx1];
//	item_s *item2	= &program()->kinds[0].item[idx2];
//	item_s temp		= *item1;	
//}
//
//} // namespace

ProgramManager *ProgramManager::instance()
{
	if (instance_ == NULL) {
		instance_ = new ProgramManager;	
	}
	return instance_;
}

ProgramManager::ProgramManager()
{
	InitProgram();
}

int ProgramManager::LoadProgram(const std::string& path)
{
	std::ifstream file;
	std::string file_path = path + kProgramFileSuffix;
	file.open(file_path.c_str(), std::ifstream::in);
	if (!file.is_open()) {
		return -1;	
	}
	file.read( (char *)program(), sizeof(program_s));
	file.close();

	file_path_ = path;
	return 0;
}

int ProgramManager::SaveProgram(const std::string& path)
{
	std::ofstream file;
	std::string file_path = path + kProgramFileSuffix;
	file.open(file_path.c_str(), std::ofstream::out | std::ofstream::trunc);
	if (!file.is_open()) {
		return -1;	
	}
	file.write( (const char *)program(), sizeof(program_s));
	file.close();
	
	file_path_ = path;
	return 0;
}



void ProgramManager::InitProgram()
{
	file_path_.clear();

	program_s *program = this->program();
	memset(program, 0xff, sizeof(program_s));

	memcpy(program->kinds[0].kind, "PROGRAM1", 16);
	memcpy(program->serialno, "SL8630", 9);
	program->kindno = 1;
	program->kinds[0].active = PM_ON;

	std::vector<std::string> dev_list = TableDriver::ParamList(PM_DEV_ATTR);
	program->kinds[0].devicetyp = 
					TableDriver::ParamCode(dev_list.at(0), PM_DEV_ATTR);
	program->kinds[0].deviceidx = 
					TableDriver::DeviceIndex(program->kinds[0].devicetyp);
	memcpy(program->devTbl, 
		   TableDriver::DeviceTable(), sizeof(devtbl_s) * MAX_DEV_ASSIGN);

	
	program->filfreq		= PM_50HZ;
	program->active 		= PM_ON;
	program->ngcounton		= PM_ON;
	program->rangemode		= 0;
	program->kindno			= 1;
	program->autoseq		= PM_OFF;
	program->warn_ngcount	= 0;
	program->hdr_logic		= 0;
	program->scan			= PM_OFF;
	program->endtime		= 0.5;
	program->offtime		= 0.4;
	program->dlm_k			= 0;

	program->alias.count			= 0;
	program->alias.list[0].typ		= 1;
	program->alias.list[0].group	= 0;

	// FIXME: when filter ON, the mlums is set to 450.0.
	// otherwise 680.0. we should change mlums when filter is changed
	program->kinds[0].filter		= PM_ON;
	program->kinds[0].ofstcancel	= PM_ON;
	program->kinds[0].ofcl_vl		= PM_ON;
	program->kinds[0].cooldown		= 0;
	program->kinds[0].cool_v1		= 0;
	program->kinds[0].cool_v2		= 0;

	for (int ch = 0; ch < MAX_CHANNEL_QTY; ++ch) {
		program->kinds[0].hue_mtemp[ch] = 25.0;
		program->kinds[0].hue_mlums[ch] = 450.0;
	}

	program->ffs		= PM_OFF;
	program->cntmode	= PM_ON;
	program->dliston	= PM_OFF;
	program->tliston	= PM_OFF;
	program->nondisp	= PM_OFF;
	program->mmode		= PM_REPEAT;

	for (int item_index = 0; item_index < MAX_ITEM_QTY; ++item_index) {
		this->program()->kinds[0].item[item_index].active = INVALID;
	}
}


int ProgramManager::InsertItem(int item_index, ushort item_code)
{
	if (item_index < 0 || item_index >= MAX_ITEM_QTY || itemCount() >= 25) {
		// index out of range, or there is gap between items,
		// or item list is full (25)
		return -1;
	}

	if (PM_ESD == item_code) {
		if (!IsEsdEnabled(item_index)) {
			// cannot insert esd item without at least 2 vf items
			return -2;
		}
	}

	MoveItems(item_index, true);

	item_s *item = &this->program()->kinds[0].item[item_index];
	item->active	= PM_ON;
	item->limit		= PM_ON;
	item->code		= item_code;
	item->suffix	= GenerateItemSuffix(item_index, item_code);
	item->ch		= 1;
	item->waittime	= TableDriver::Range(PM_T9999ms)->lowlimit;
	item->brange	= 
		TableDriver::ParamCode( 
				TableDriver::ItemRangeList(item_code, PM_BRANGE_ATTR).at(0), 
				PM_BRANGE_ATTR);
	item->bvalue	= TableDriver::Range(item->brange)->lowlimit;
	item->mrange	=
		TableDriver::ParamCode( 
				TableDriver::ItemRangeList(item_code, PM_MRANGE_ATTR).at(0), 
				PM_MRANGE_ATTR);
	item->mlowlimit	= TableDriver::Range(item->mrange)->lowlimit;
	item->mhilimit	= TableDriver::Range(item->mrange)->hilimit;
	item->vlimit	= TableDriver::Range(PM_L9_99V)->hilimit;
	item->filter	= PM_FOFF;	//TableDriver::ParamList(PM_FIL_ATTR);

	if (PM_ESD == item_code) {
		InitEsdConfig(item_index);
	} else if (TableDriver::IsMasterItem(item_code)) {
		InitMasterConfig(item_index);
	}	

	// TODO: care about ESD
	
	return 0;
}


int ProgramManager::RemoveItem(int item_index)
{
	if (!IsValidItem(item_index)) {
		return -1;
	}
	MoveItems(item_index, false);

	// TODO: care about ESD
	
	return 0;
}


program_s *ProgramManager::program() const
{
	return &prog;
}


item_s *ProgramManager::item(int item_index) const
{
	if (IsValidItem(item_index)) {
		return &prog.kinds[0].item[item_index];
	}
	return NULL;
}

int ProgramManager::itemCount() const
{
	int itm_idx = 0;
	while (IsValidItem(itm_idx)) {
		++itm_idx;
	}
	return itm_idx;
}


bool ProgramManager::IsChangedAndUnsaved() const
{
	if (file_path_.empty()) {
		return true;
	}
	
	// read file and compare it with current program
	program_s program;
	std::string file_path = file_path_ + kProgramFileSuffix;
	std::ifstream file;
	file.open(file_path.c_str(), std::ifstream::in);
	file.read((char *)&program, sizeof(program_s));
	file.close();

	return (memcmp(&program, this->program(), sizeof(program_s)) != 0);
}



int ProgramManager::setDeviceType(ushort dev_code)
{
	const prm_tbl_s *prm = TableDriver::Param(dev_code);
	if (NULL == prm) {
		return -1;
	}
	if (prm->attr != PM_DEV_ATTR) {
		return -2;
	}
	program()->kinds[0].devicetyp = dev_code;
	program()->kinds[0].deviceidx = TableDriver::DeviceIndex(dev_code);
	return 0;
}
ushort ProgramManager::deviceType() const
{
	return program()->kinds[0].devicetyp;
}


int ProgramManager::setMeasOptMeasMode(ushort meas_mode)
{
	program()->mmode = meas_mode;
	return 0;
}
ushort ProgramManager::measOptMeasMode() const
{
	return program()->mmode;
}
int ProgramManager::setMeasOptFfs(bool enable)
{
	program()->ffs = enable ? PM_ON : PM_OFF;
	return 0;
}
bool ProgramManager::measOptFfs() const
{
	return (PM_ON == program()->ffs);
}
int ProgramManager::setMeasOptCntClr(bool enable)
{
	program()->cntmode = enable ? PM_ON : PM_OFF;
	return 0;
}
bool ProgramManager::measOptCntClr() const
{
	return (PM_ON == program()->cntmode);
}
int ProgramManager::setMeasOptDataFile(bool enable)
{
	program()->dliston = enable ? PM_ON : PM_OFF;
	return 0;
}
bool ProgramManager::measOptDataFile() const
{
	return (PM_ON == program()->dliston);
}



int ProgramManager::setOptEndTime(double end_time)
{
	const rng_tbl_s *range = TableDriver::Range(PM_T99_9ms);
	if (end_time < range->lowlimit || range->hilimit < end_time) {
		// out of range
		return -1;
	}
	program()->endtime = end_time;
	return 0;
}
double ProgramManager::optEndTime() const
{
	return program()->endtime;
}
int ProgramManager::setOptOffTime(double off_time)
{
	const rng_tbl_s *range = TableDriver::Range(PM_T99_9ms);
	if (off_time < range->lowlimit || range->hilimit < off_time) {
		// out of range
		return -1;
	}
	program()->offtime = off_time;
	return 0;
}
double ProgramManager::optOffTime() const
{
	return program()->offtime;
}
int ProgramManager::setOptWarnNgCnt(int warn_ng_cnt)
{
	const rng_tbl_s *range = TableDriver::Range(PM_COUNT);
	if (warn_ng_cnt < range->lowlimit || range->hilimit < warn_ng_cnt) {
		return -1;
	}
	program()->warn_ngcount = warn_ng_cnt;
	return 0;
}
int ProgramManager::optWarnNgCnt() const
{
	return program()->warn_ngcount;
}
int ProgramManager::setOptFilterFreq(ushort freq_code)
{
	const prm_tbl_s *prm = TableDriver::Param(freq_code);
	if (NULL == prm) {
		// not a valid param code
		return -1;
	}
	if (prm->attr != PM_FILFREQ_ATTR) {
		// not a fil freq code
		return -2;
	}
	program()->filfreq = freq_code;
	return 0;
}
ushort ProgramManager::optFilterFreq() const
{
	return program()->filfreq;
}
int ProgramManager::setOptHdrLogic(int hdr_logic)
{
	const rng_tbl_s *range = TableDriver::Range(PM_LOGIC);
	if (hdr_logic < range->lowlimit || range->hilimit < hdr_logic) {
		return -1;
	}
	program()->hdr_logic = hdr_logic;
	return 0;
}
int ProgramManager::optHdrLogic() const
{
	return program()->hdr_logic;
}
int ProgramManager::setOptDlmCoef(double coef)
{
	const rng_tbl_s *range = TableDriver::Range(PM_DLMADJ);
	if (coef < range->lowlimit || range->hilimit < coef) {
		return -1;
	}
	program()->dlm_k = coef;
	return 0;
}
double ProgramManager::optDlmCoef() const
{
	return program()->dlm_k;
}
int ProgramManager::setOptSelStep(bool enable)
{
	program()->selstep = enable ? PM_ON : PM_OFF;
	return 0;
}
bool ProgramManager::optSelStep() const
{
	return (PM_ON == program()->selstep);
}

int ProgramManager::itemIndex(const std::string& item_name, char item_suffix)
{
	ushort item_code = TableDriver::ParamCode(item_name, PM_ITEM_ATTR);
	int item_cnt = itemCount();
	for (int item_index = 0; item_index < item_cnt; ++item_index) {
		if ( item_code == this->item(item_index)->code 
			 && item_suffix == this->item(item_index)->suffix) {
			return item_index;
		}
	}
	return -1;
}

ushort ProgramManager::itemCode(int item_index) const
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return 0;
	} 
	return item->code;
}
char ProgramManager::itemSuffix(int item_index) const
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return 0;
	}
	return item->suffix;
}
int ProgramManager::setItemChannel(int item_index, int ch)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	if (ch < 1 || MAX_CHANNEL_QTY < ch) {
		return -2;
	}
	item->ch = ch;
	return 0;
}
int ProgramManager::itemChannel(int item_index) const
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return 0;
	}
	return item->ch;
}
int ProgramManager::setItemWaitTime(int item_index, double wait_time)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	const rng_tbl_s *range = TableDriver::Range(PM_T9999ms);
	if (wait_time < range->lowlimit || range->hilimit < wait_time) {
		return -2;
	}
	item->waittime = wait_time;
	return 0;
}
double ProgramManager::itemWaitTime(int item_index) const
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return 0.0;
	}
	return item->waittime;
}
int ProgramManager::setItemBiasRng(int item_index, ushort rng_code)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	const prm_tbl_s *prm = TableDriver::Param(rng_code);
	if (prm->attr != PM_BRANGE_ATTR) {
		// not a bias range code,
		// here I don't check it strictly according to the item code
		return -2;
	}
	item->brange = rng_code;
	return 0;
}
ushort ProgramManager::itemBiasRng(int item_index) const
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return 0;
	}
	return item->brange;
}
int ProgramManager::setItemBiasVal(int item_index, double val)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	const rng_tbl_s *range = TableDriver::Range(item->brange);
	if (val < range->lowlimit || range->hilimit < val) {
		return -2;
	}
	item->bvalue = val;
	return 0;
}
double ProgramManager::itemBiasVal(int item_index)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return 0.0;
	}
	return item->bvalue;
}
int ProgramManager::setItemMeasRng(int item_index, ushort rng_code)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	const prm_tbl_s *prm = TableDriver::Param(rng_code);
	if (prm->attr != PM_MRANGE_ATTR) {
		return -2;
	}
	item->mrange = rng_code;
	return 0;
}
ushort ProgramManager::itemMeasRng(int item_index) const
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return 0;
	}
	return item->mrange;
}
int ProgramManager::setItemMeasVal(int item_index, bool lower, double val)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	const rng_tbl_s *range = TableDriver::Range(item->mrange);
	if (val < range->lowlimit || range->hilimit < val) {
		return -2;
	}
	// TODO: here I don't check if the lower > upper,
	// maybe it's a problem, be careful
	double *set_val = lower ? &item->mlowlimit : &item->mhilimit;
	*set_val = val;
	return 0;
}
double ProgramManager::itemMeasVal(int item_index, bool lower) const
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return 0.0;
	}
	return (lower ? item->mlowlimit : item->mhilimit);
}
int ProgramManager::setItemVolLimit(int item_index, double limit)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	// TODO: TableDriver should provide a function like: 
	// CheckRangeValue(double val, ushort rng_code)
	// to check if the value is inside the range...
	// fix this when refactoring
	const rng_tbl_s *range = TableDriver::Range(PM_L9_99V);
	if (limit < range->lowlimit || range->hilimit < limit) {
		return -2;
	}
	item->vlimit = limit;
	return 0;
}
double ProgramManager::itemVolLimit(int item_index)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return 0.0;
	}
	return item->vlimit;
}
int ProgramManager::setItemLimitJudge(int item_index, bool enable)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	item->limit = enable ? PM_ON : PM_OFF;
	return 0;
}
bool ProgramManager::itemLimitJudge(int item_index) const
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return false;
	}
	return (PM_ON == item->limit);
}
int ProgramManager::setItemFilterOpt(int item_index, ushort fil_code)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	if (TableDriver::Param(fil_code)->attr != PM_FIL_ATTR) {
		return -2;
	}
	item->filter = fil_code;
	return 0;
}
ushort ProgramManager::itemFilterOpt(int item_index)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return 0;
	}
	return item->filter;
}

std::vector<int> ProgramManager::RefItemList(int item_index) const
{
	std::vector<int> result;
	for (int itm_idx = 0; itm_idx < item_index; ++itm_idx) {
		if (this->item(itm_idx)->code == PM_VF
			|| this->item(itm_idx)->code == PM_ESD) {
			result.push_back(itm_idx);
		}
	}
	return result;
}

int ProgramManager::setEsdRefItem(int itm_idx, int ref_idx, int ref_itm_idx)
{
	item_s *item = this->item(itm_idx);
	if (NULL == item) {
		return -1;
	}
	if (ref_idx < 0 || ref_idx > 1) {
		return -2;
	}
	if (item->code != PM_ESD) {
		return -3;
	}
	item_s *ref_item = this->item(ref_itm_idx);
	if (NULL == ref_item) {
		return -4;
	}
	if (ref_item->code != PM_ESD && ref_item->code != PM_VF) {
		// only can ref to VF or ESD item
		return -5;
	}
	item->det.esd[ref_idx].ino		= ref_itm_idx + 1;	
	item->det.esd[ref_idx].code		= ref_item->code;
	item->det.esd[ref_idx].suffix	= ref_item->suffix;
	item->det.esd[ref_idx].ch		= ref_item->ch;
	return 0;
}
int ProgramManager::esdRefItem(int itm_idx, int ref_idx) const
{
	item_s *item = this->item(itm_idx);
	if (NULL == item) {
		return -1;	
	}
	if (ref_idx < 0 || ref_idx > 1) {
		return -2;
	}
	if (item->code != PM_ESD) {
		return -3;
	}
	return (item->det.esd[ref_idx].ino - 1);
}

int ProgramManager::setHueAutoRange(int item_index, bool enable)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	if (!TableDriver::IsMasterItem(item->code)) {
		return -2;
	}
	item->det.hue.autorange = enable ? PM_ON : PM_OFF;
	return 0;
}
bool ProgramManager::hueAutoRange(int item_index) const
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return false;
	}
	// TODO:
	// THINK!: because of return type, cannot check it as strictly
	// as the set functions, how to solve this?
	return (PM_ON == item->det.hue.autorange);
}
int ProgramManager::setHueOfstCancel(int item_index, bool enable)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	if (!TableDriver::IsMasterItem(item->code)) {
		return -2;
	}
	item->det.hue.ofstcancel = enable ? PM_ON : PM_OFF;
	return 0;
}
bool ProgramManager::hueOfstCancel(int item_index) const
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return false;
	}
	return (PM_ON == item->det.hue.ofstcancel);
}
int ProgramManager::setHuePreAmpRng(
		int item_index, int pre_amp_idx, ushort rng_code)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	if (pre_amp_idx < 0 || pre_amp_idx > 2) {
		return -2;
	}
	const prm_tbl_s *prm = TableDriver::Param(rng_code);
	if (prm->attr != PM_HUERNG_ATTR) {	// TODO: should check more strictly
		return -3;
	}
	if (0 == pre_amp_idx) {
		item->det.hue.mrng1 = rng_code;
	} else if (1 == pre_amp_idx) {
		item->det.hue.mrng2 = rng_code;
	} else {
		item->det.hue.mrng3 = rng_code;
	}
	return 0;
}
ushort ProgramManager::huePreAmpRng(int item_index, int pre_amp_idx) const
{
	item_s *item = this->item(item_index);
	if (NULL == item || pre_amp_idx < 0 || pre_amp_idx > 2) {
		return 0;
	}
	if (0 == pre_amp_idx) {
		return item->det.hue.mrng1;
	} else if (1 == pre_amp_idx) {
		return item->det.hue.mrng2;
	} else {
		return item->det.hue.mrng3;
	}
}
int ProgramManager::setHueAmpScf(int item_index, int amp_idx, ushort scf_code)
{
	item_s *item = this->item(item_index);
	if (NULL == item) {
		return -1;
	}
	if (amp_idx < 0 || amp_idx > 2) {
		return -2;
	}
	const prm_tbl_s *prm = TableDriver::Param(scf_code);
	if (prm->attr != PM_HUESCF_ATTR) {
		return -3;
	}
	if (0 == amp_idx) {
		item->det.hue.scf1 = scf_code;
	} else if (1 == amp_idx) {
		item->det.hue.scf2 = scf_code;
	} else {
		item->det.hue.scf3 = scf_code;
	}
	return 0;
}
ushort ProgramManager::hueAmpScf(int item_index, int amp_idx) const
{
	item_s *item = this->item(item_index);
	if (NULL == item || amp_idx < 0 || amp_idx > 2) {
		return 0;
	}
	if (0 == amp_idx) {
		return item->det.hue.scf1;
	} else if (1 == amp_idx) {
		return item->det.hue.scf2;
	} else {
		return item->det.hue.scf3;
	}
}


bool ProgramManager::IsValidItem(int item_index) const
{
	if (item_index < 0 || item_index >= MAX_ITEM_QTY) {
		return false;
	}
	return (this->program()->kinds[0].item[item_index].active == PM_ON);
}

char ProgramManager::GenerateItemSuffix(int item_idx, ushort item_code)
{
	item_s *item_list = program()->kinds[0].item;
	
	bool found = true;
	char suffix = 0;
	for (int sfx_idx = 0; sfx_idx < MAX_ITEM_QTY; ++sfx_idx) {
		if (sfx_idx < 9) {
			suffix = '1' + sfx_idx;			// suffix: '1' ~ '9'
		} else {
			suffix = 'a' + sfx_idx - 9;		// suffix: 'a' ~ 'p'
		}

		found = true;
//		for (int item_index = 0; item_index < MAX_ITEM_QTY 
//			 && item_list[item_index].active == PM_ON; ++item_index) {
		for (int item_index = 0; item_index < itemCount(); ++item_index) {
			if (item_index == item_idx) {	// do not compare with itself
				continue;
			}

			if (item_list[item_index].code == item_code 
				&& item_list[item_index].suffix == suffix) {
				found = false;
				break;
			}
		}
		if (found) {
			return suffix;
		}
	}
	return 0;
}


bool ProgramManager::IsEsdEnabled(int item_index) const
{
	item_s *item = NULL;
	int vf_count = 0;
	// TODO: should judge if item is active,
	// maybe I've made this mistake some place else, check later 
	//for (int itm_idx = 0; itm_idx < MAX_ITEM_QTY; ++itm_idx) {	
	int itm_idx = 0;
	item = &program()->kinds[0].item[itm_idx];	
//	while (itm_idx < MAX_ITEM_QTY && PM_ON == item->active) {
	while (itm_idx < item_index) {
		// just check items before this item,
		// because we can only REF to items before the ESD item
		if (PM_VF == item->code) {
			if (++vf_count >= 2) {
				return true;
			}
		}
		++itm_idx;
		++item;
	}
	return false;
}



void ProgramManager::InitEsdConfig(int item_index)
{
	int ref_item[2] = { INVALID, INVALID };
	int itm_idx = 0;
	int vf_index = 0;
	while (itm_idx < item_index && vf_index < 2) {
		if (PM_VF == this->item(itm_idx)->code) {
			ref_item[vf_index++] = itm_idx + 1;
		}
		++itm_idx;
	}

	esdp_s *esd = this->item(item_index)->det.esd;
	for ( int esd_idx = 0 ; esd_idx < 2; ++esd_idx) {
		esd[esd_idx].ino	= ref_item[esd_idx];
		esd[esd_idx].code	= this->item(ref_item[esd_idx] - 1)->code;
		esd[esd_idx].suffix = this->item(ref_item[esd_idx] - 1)->suffix;
		esd[esd_idx].ch		= this->item(ref_item[esd_idx] - 1)->ch;
	}
}

void ProgramManager::InitMasterConfig(int item_index)
{
	huep_s *hue = &this->item(item_index)->det.hue;	
//	if (TableDriver::IsHueItem(item_code)) {
//	} else if (TableDriver::IsVlItem(item_code)) {
//	} else if (TableDriver::IsDlmItem(item_code)) {
//	}
	hue->autorange	= PM_ON;
	hue->ofstcancel = PM_ON;
	hue->mrng1		= PM_H100_0nA;
	hue->scf1		= PM_HX1;
	hue->mrng2		= PM_H100_0nA;
	hue->scf2		= PM_HX1;
	hue->mrng3		= PM_H100_0nA;
	hue->scf3		= PM_HX1;
}


void ProgramManager::MoveItems(int item_index, bool insert)
{
	int item_count = itemCount();
	if (insert) {

		// NOTE!: We cannot use itemCount() here, because every time we move last
		// item forward and count the items again, the result is last value +1 !!
		// I've made the same mistake once before...
//		for (int itm_idx = itemCount(); itm_idx > item_index; --itm_idx) {

		// it's an insertion, we should move forward
		for (int itm_idx = item_count; itm_idx > item_index; --itm_idx) {
			memcpy(&program()->kinds[0].item[itm_idx], 
				   &program()->kinds[0].item[itm_idx-1], sizeof(item_s));
		}
	} else {
		// deletion, move backward
		for (int itm_idx = item_index; itm_idx < item_count - 1; ++itm_idx) {
			memcpy(&program()->kinds[0].item[itm_idx], 
				   &program()->kinds[0].item[itm_idx+1], sizeof(item_s));
		}
		// now the last item is at itemCount()-2, so delete the excess one
		program()->kinds[0].item[item_count - 1].active = INVALID;
	}	
}


void ProgramManager::PrintAllItems() const
{
	std::cout << "this program has " << itemCount() << " items #####\n";
	for (int idx = 0; idx < itemCount(); ++idx) {
		std::cout << "item " << idx << " ------->:\n";
		std::cout << "item code = " << item(idx)->code << std::endl;
	}
}
